//
//  Lattice.cpp
//  phi_four
//
//  Created by Snir Gazit on 3/8/12.
//  Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//

#include "Lattice.h"
// Site Template Functions

//Lattice
void Lattice::initSquare(boost::array<int, DIM> const &latSize) {
	cordinationNumber_ = 2 * DIM;
	siteLattice_.init(latSize);
	BondLattice_.init(latSize);
	latSize_ = latSize;
	SiteIterIndex iter = siteLattice_.beginIndex();
	for (; iter != siteLattice_.endIndex(); iter++) {
		IndexType currentPos = iter.getIndex();
		//iter->siteIndex_ = currentPos;
		iter->totalBondsArray_.fill(0);
		iter->totalBonds_ = 0;
		for (int d = 0; d < DIM; d++) {
			// XY Bonds
			BondType& currentbond = BondLattice_(currentPos)[d];
			currentbond.fill(0);
			// Update NN
			boost::array<int, DIM> nPos = currentPos;
			if (nPos[d] == (latSize_[d] - 1)) {
				nPos[d] = 0;
			} else
				nPos[d]++;
			SiteIter nIter = siteLattice_.iterByPos(nPos);
			// XY Bonds
			iter->bondArray_[d] = &currentbond;
			nIter->bondArray_[DIM + d] = &currentbond;
			// Nearest neighbor
			iter->nSiteArray_[d] = nIter;
			nIter->nSiteArray_[DIM + d] = iter.getIter();
		}
	}
//    for (; iter != siteLattice_.endIndex(); iter++) {
//        std::sort(iter->bondArray_.begin(), iter->bondArray_.end(),
//                  [](BondData bond1,BondData bond2)->bool
//        {
//            if (bond1.dir>bond2.dir) return true;
//            else
//                if (bond.moveDir==LEFT return true
//                    else return false;
//        })
//    }
}

void Lattice::init(boost::array<int, DIM> const &latSize) {
	this->initSquare(latSize);
	ira_ = siteLattice_.begin();
	masha_ = siteLattice_.begin();
//	ira_->totalBonds_ = 1;
//	string outPath = "/home/snirgaz";
//	this->dump(outPath);
//	this->read(outPath);
}

int Lattice::getNumOfSites() const {
	return siteLattice_.totalSize();
}

Lattice::SiteIter Lattice::getSite(int distanceFromStart) {
	return siteLattice_.begin() + distanceFromStart;
}
boost::array<int, DIM> Lattice::getSizes() {
	return latSize_;
}
bool Lattice::testConfiguration(int &totalBonds) {
	totalBonds = 0;
	SiteIterIndex it = siteLattice_.beginIndex();
	for (; it != siteLattice_.endIndex(); it++) {
		Site::BondArray bondArray = it->bondArray_;
		for (int n = 0; n < NF; n++) {
			int totalBondsN = 0;
			for (int d = 0; d < 2 * DIM; d++) {
				BondValType bond = (*bondArray[d])[n];
				totalBonds += bond;
				totalBondsN += bond;
			}
			if ((!(isClosed_)) && (sectorType_ == n)) {
				if (it.getIter() == ira_) {
					totalBondsN++;
				}
				if (it.getIter() == masha_) {
					totalBondsN++;
				}
			}
			if ((totalBondsN % 2) != 0) {
				IndexType indexc = it.getIndex();
				cout << indexc[0] << " " << indexc[1] << " " << indexc[2];
				return false;
			}
			if (totalBondsN / 2 != it->totalBondsArray_[n]) {
				IndexType indexc = it.getIndex();
				cout << indexc[0] << " " << indexc[1] << " " << indexc[2];
				return false;
			}
		}
	}
	return true;
}
Lattice::BondArrayIter Lattice::BondBegin() {
	return BondLattice_.begin();
}
Lattice::BondArrayIter Lattice::BondEnd() {
	return BondLattice_.end();
}
Lattice::SiteIter Lattice::siteBegin() {
	return siteLattice_.begin();
}
Lattice::SiteIter Lattice::siteEnd() {
	return siteLattice_.end();
}
Lattice::SiteIterIndex Lattice::siteBeginIndex() {
	return siteLattice_.beginIndex();
}
Lattice::SiteIterIndex Lattice::siteEndIndex() {
	return siteLattice_.endIndex();
}
void Lattice::dump(string fileName, bool final, ofstream *outFile) {
	try {
		bool testConf = testConfiguration(totalBonds_);
		if (!testConf)
			throw "Bad Configuration Dumping";
	} catch (string &e) {
		*outFile << e;
		exit(1);
	}
	if (final)
		fileName += "/Latdat.zip";
	else
		fileName += "/LatdatTemp.zip";
	namespace io = boost::iostreams;
	namespace ar = boost::archive;
	ofstream file(fileName.c_str(),
			std::ios::out | std::ios::binary | std::ios::trunc);
	io::filtering_ostream out;
	out.push(io::zlib_compressor());
	out.push(file);
	ar::text_oarchive oa(out);
//	ofstream file(fileName.c_str(), ios_base::out);
//	ar::text_oaromchive oa(file);
	oa << *this;

}
void Lattice::read(string fileName) {
	try {
		fileName += "/Latdat.zip";
		namespace io = boost::iostreams;
		namespace ar = boost::archive;
		ifstream file(fileName.c_str(), ios_base::in | ios_base::binary);
		io::filtering_istream in;
		in.push(io::zlib_decompressor());
		in.push(file);
		ar::text_iarchive oi(in);
		oi >> *this;
	} catch (string &s) {
		cout << s;
		exit(1);
	}
}

IndexType Lattice::iraIndex() {
	return siteLattice_.posByIter(ira_);
}
IndexType Lattice::mashaIndex() {
	return siteLattice_.posByIter(masha_);
}
